package app_kvEcs;


import java.io.IOException;

import org.apache.log4j.Level;

import logger.LogSetup;
import utils.ClientConnectionUtil;
import app_kvClient.Constants;
import common.messages.KVMessage;
import common.messages.KVMessageImpl;

public class KVStoreECS  {

	private LogSetup configLogger; // client log
	
	private String serverAddress = "";
	private int serverPort = 0;	
	
	private boolean CONNECTION_ENABLED = true;
	
	private ClientConnectionUtil connectionUtil; // Connection object
	/**
	 * Initialize KVStore with address and port of KVServer
	 * @param address the address of the KVServer
	 * @param port the port of the KVServer
	 */
	public KVStoreECS(String address, int port) {
		this(address,port,true);
	}
	
	public KVStoreECS(String address, int port, boolean loggingEnabled){
		this.serverAddress = address;
		this.serverPort = port;
		connectionUtil = new ClientConnectionUtil();
		if(loggingEnabled)
			setupLogger(true);
		else
			setupLogger(false);
	}
	
	public void enableConnection(){
		CONNECTION_ENABLED = true;
	}
	
	public void disableConnection(){
		CONNECTION_ENABLED = false;
	}
	
	private void setupLogger(boolean loggingEnabled) {
		try {
			Level level = Level.ALL;
			if(!loggingEnabled)
				level = Level.OFF;
			configLogger = new LogSetup(Constants.LOGGERS.CONFIG_LOGGER, level);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public KVMessage connect() throws Exception {
		if(!CONNECTION_ENABLED)
			return null;
		try {
			boolean connectionSucceed = connectionUtil.initSocketConnection(serverAddress, serverPort);
			configLogger.infoLog(Constants.MESSAGES.CONNECTION_SUCCEEDED, null);
			return getServerResponse();
		} catch (Exception ex){
			configLogger.infoLog(Constants.ERRORS.CONNECTION_NOT_SUCCEEDED, ex);
			throw ex;
		}
	}

	public void disconnect() {
		try {
			if(connectionUtil.isConnectionEstablished()){
				connectionUtil.closeConnection();
				configLogger.infoLog("successfully disconnected from the host",null);
			}
		} catch (IOException exc) {
			configLogger.errorLog("Caught when tried to close the connection", exc);
		}
	}


	/**
	 * This method gets the server response and converts it into ASCII format
	 * 
	 * @exception IOException
	 *                when cannot connect successfully with the server
	 */
	private KVMessage getServerResponse() {
		if(!CONNECTION_ENABLED)
			return null;
		byte[] responseBytes;
		KVMessage response = null;
		try {
			responseBytes = connectionUtil.receive();
			response = new KVMessageImpl(responseBytes);
			configLogger.infoLog(response.toString(),null);
		} catch (IOException exc) {
			configLogger.errorLog("An error occured while getting response from the server", exc);
			System.out.print(Constants.ERRORS.CONNECTION_NOT_SUCCEEDED);
		}
		
		return response;
	}
		
	public KVMessage initMetadata(String metadada) throws Exception {
		if(!CONNECTION_ENABLED)
			return null;
		KVMessageImpl message = new KVMessageImpl(metadada, System.currentTimeMillis()+"", KVMessage.StatusType.INIT_SERVER);
		byte[] messageBytes = message.toBytes();
		try {
			connectionUtil.send(messageBytes);
			configLogger.infoLog(message.toString(),null);
		} catch (Exception ex) {
			configLogger.infoLog(Constants.ERRORS.RESPONSE_ERROR, ex);
			throw ex;
		}
		
		return getServerResponse();
	}
	
	public KVMessage updateMetadata(String metadata) throws Exception {
		if(!CONNECTION_ENABLED)
			return null;
		KVMessageImpl message = new KVMessageImpl(metadata, System.currentTimeMillis()+"", KVMessage.StatusType.UPDATE_META);
		byte[] messageBytes = message.toBytes();
		try {
			connectionUtil.send(messageBytes);
			configLogger.infoLog(message.toString(),null);
		} catch (Exception ex) {
			configLogger.infoLog(Constants.ERRORS.RESPONSE_ERROR, ex);
			throw ex;
		}
		
		return getServerResponse();
	}
	
	public KVMessage updateRange(String startRange, String endRange) throws Exception {
		if(!CONNECTION_ENABLED)
			return null;
		KVMessageImpl message = new KVMessageImpl(startRange, endRange, KVMessage.StatusType.UPDATE_RANGE);
		byte[] messageBytes = message.toBytes();
		try {
			connectionUtil.send(messageBytes);
			configLogger.infoLog(message.toString(),null);
		} catch (Exception ex) {
			configLogger.infoLog(Constants.ERRORS.RESPONSE_ERROR, ex);
			throw ex;
		}
		
		return getServerResponse();
	}
	
	public KVMessage start() throws Exception {
		if(!CONNECTION_ENABLED)
			return null;
		KVMessageImpl message = new KVMessageImpl("", System.currentTimeMillis()+"", KVMessage.StatusType.START_SERVER);
		byte[] messageBytes = message.toBytes();
		try {
			connectionUtil.send(messageBytes);
			configLogger.infoLog(message.toString(),null);
		} catch (Exception ex) {
			configLogger.infoLog(Constants.ERRORS.RESPONSE_ERROR, ex);
			throw ex;
		}
		
		return getServerResponse();
	}
	
	public boolean isConnectionEstablished() {
		return connectionUtil.isConnectionEstablished();
	}

	public KVMessage stop()  throws Exception{
		if(!CONNECTION_ENABLED)
			return null;
		KVMessageImpl message = new KVMessageImpl("", System.currentTimeMillis()+"", KVMessage.StatusType.STOP_SERVER);
		byte[] messageBytes = message.toBytes();
		try {
			connectionUtil.send(messageBytes);
			configLogger.infoLog(message.toString(),null);
		} catch (Exception ex) {
			configLogger.infoLog(Constants.ERRORS.RESPONSE_ERROR, ex);
			throw ex;
		}
		
		return getServerResponse();
	}

	public KVMessage shutDown()  throws Exception{
		if(!CONNECTION_ENABLED)
			return null;
		KVMessageImpl message = new KVMessageImpl("", System.currentTimeMillis()+"", KVMessage.StatusType.SHUTDOWN_SERVER);
		byte[] messageBytes = message.toBytes();
		try {
			connectionUtil.send(messageBytes);
			configLogger.infoLog(message.toString(),null);
		} catch (Exception ex) {
			configLogger.infoLog(Constants.ERRORS.RESPONSE_ERROR, ex);
			throw ex;
		}
		
		return getServerResponse();
	}
	 
	public KVMessage lockWrite()  throws Exception{
		if(!CONNECTION_ENABLED)
			return null;
		KVMessageImpl message = new KVMessageImpl("", System.currentTimeMillis()+"", KVMessage.StatusType.LOCK_WRITE);
		byte[] messageBytes = message.toBytes();
		try {
			connectionUtil.send(messageBytes);
			configLogger.infoLog(message.toString(),null);
		} catch (Exception ex) {
			configLogger.infoLog(Constants.ERRORS.RESPONSE_ERROR, ex);
			throw ex;
		}
		
		return getServerResponse();
	}
	
	public KVMessage unLockWrite()  throws Exception{
		if(!CONNECTION_ENABLED)
			return null;
		KVMessageImpl message = new KVMessageImpl("", System.currentTimeMillis()+"", KVMessage.StatusType.UNLOCK_WRITE);
		byte[] messageBytes = message.toBytes();
		try {
			connectionUtil.send(messageBytes);
			configLogger.infoLog(message.toString(),null);
		} catch (Exception ex) {
			configLogger.infoLog(Constants.ERRORS.RESPONSE_ERROR, ex);
			throw ex;
		}
		
		return getServerResponse();
	}
	
	public KVMessage moveData(String destination, String startRange, String endRange)  throws Exception{
		if(!CONNECTION_ENABLED)
			return null;
		KVMessageImpl message = new KVMessageImpl(destination, startRange+":"+endRange+":"+System.currentTimeMillis()+"", KVMessage.StatusType.MOVE_DATA);
		byte[] messageBytes = message.toBytes();
		try {
			connectionUtil.send(messageBytes);
			configLogger.infoLog(message.toString(),null);
		} catch (Exception ex) {
			configLogger.infoLog(Constants.ERRORS.RESPONSE_ERROR, ex);
			throw ex;
		}
		
		return getServerResponse();
	}
	
	
	
}
